﻿using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Collections;
using System.ComponentModel;
using System;

namespace Xww.WindowsForm.Controls {
    public partial class DragableListbox  : ListBox {
        public DragableListbox() :base() {            
            InitControl();
        }
        private void InitControl() {
            base.AllowDrop = true;
            this.HintPen = Pens.Red;
            this.BackColorLostFoucus = SystemColors.InactiveCaption;
            base.SelectionMode = SelectionMode.MultiExtended;
        }
        Color orginBackColor;
        Color backColorLostFocus;
        [Category("Appearance")]
        public Color BackColorLostFoucus {
            get { return backColorLostFocus; }
            set { backColorLostFocus = value; }
        }
        Pen hintPen;
        [Browsable(false)]
        public Pen HintPen { get { return hintPen; } set { hintPen = value; } }
        [Browsable(false)]
        public IList ListSource {
            get { return this.DataSource as IList; }
        }
        [DefaultValue(false)]
        public bool AllowFileDrag { get; set; }
        private bool IsItemDrag { get; set; }

        #region overide methods
        protected override void OnControlAdded(ControlEventArgs e) {
            base.OnControlAdded(e);
            orginBackColor = this.BackColor;
            if (!this.Focused) {
                this.BackColor = this.BackColorLostFoucus;
            }
        }
        protected override void OnGotFocus(System.EventArgs e) {
            base.OnGotFocus(e);
            this.BackColor = orginBackColor;
        }
        protected override void OnLostFocus(System.EventArgs e) {
            base.OnLostFocus(e);
            this.BackColor = this.BackColorLostFoucus;
        }
        protected override void OnKeyDown(KeyEventArgs e) {
            base.OnKeyDown(e);
            if (e.KeyCode == Keys.Delete && this.SelectedItems.Count > 0) {
                DeleteSelectedItems();
            }
            else if (e.Control) {
                int count = this.Items.Count;
                if (e.KeyCode == Keys.A) { // 全选
                    for (int i = 0; i < count; i++) {
                        this.SetSelected(i, true);
                    }
                } if (e.KeyCode == Keys.I) {    // 反选
                    for (int i = 0; i < count; i++) {
                        this.SetSelected(i, !this.GetSelected(i));
                    }
                }
            }
        }

        protected override void OnMouseDown(MouseEventArgs e) {
            base.OnMouseDown(e);
            if (this.SelectedItems.Count == 0 || this.SelectedItems.Count == this.Items.Count) {
                return;
            }
            IsItemDrag = (e.Button == MouseButtons.Right);
            if (e.Button == MouseButtons.Right) {
                this.DoDragDrop(this.SelectedItems, DragDropEffects.All);
                setDragParams();
            }
        }

        protected override void OnDragEnter(DragEventArgs drgevent) {
            base.OnDragEnter(drgevent);
            drgevent.Effect = DragDropEffects.All;
        }

        protected override void OnDragOver(DragEventArgs drgevent) {
            base.OnDragOver(drgevent);
            p.X = drgevent.X;
            p.Y = drgevent.Y;
            p = PointToClient(p);
            currentIndex = GetCurrentIndex();
            if (lastSelectedIndex != currentIndex) {
                DrawHintLine();
            }
            lastSelectedIndex = currentIndex;
        }

        [EditorBrowsable(EditorBrowsableState.Always)]
        [Browsable(true)]
        public event EventHandler<FileDragDropEventArgs> FileDragDrop;
        protected override void OnDragDrop(DragEventArgs drgevent) {
            base.OnDragDrop(drgevent);
            if (!IsItemDrag) {
                if (AllowFileDrag) {
                    string[] fileName = (string[])drgevent.Data.GetData(DataFormats.FileDrop);
                    if (fileName == null) { return; }
                    if (FileDragDrop != null) {
                        FileDragDrop(this, new FileDragDropEventArgs(fileName));
                    }
                }
                else { return; }
            }
            graphic = null;
            RefreshListBox();

            setDragParams();
            int dragIndex = currentIndex == -1 ? 0 : currentIndex;

            var deleted = DeleteAndGetSelectedItems();
            int skip = 0;
            for (int i = 0; i < selectedIndex.Length; i++) {
                if (selectedIndex[i] < currentIndex) {
                    skip++;
                }
            }
            dragIndex -= skip;
            currentIndex = dragIndex;
            foreach (var item in deleted) {
                this.ListSource.Insert(dragIndex, item);
                dragIndex++;
            }
            this.ClearSelected();
            this.SelectedItems.Clear();
            foreach (var item in deleted) {
                this.SelectedItems.Add(item);
            }
            Refresh();
        }
        #endregion

        public void DeleteSelectedItems() {
            int count = this.SelectedIndices.Count;
            int[] index =new int[count];
            this.SelectedIndices.CopyTo(index, 0);
            this.BeginUpdate();
            if (this.DataSource == null) {
                for (int i = count - 1; i >= 0; i--) {
                    this.Items.RemoveAt(index[i]);
                }
            }
            else {
                for (int i = count - 1; i >= 0; i--) {
                    this.ListSource.RemoveAt(index[i]);
                }
            }
            this.EndUpdate();
        }
        public List<object> DeleteAndGetSelectedItems() {
            List<object> rst = new List<object>(this.SelectedItems.Count);
            foreach (var item in this.SelectedItems) {
                rst.Add(item);
            }
            foreach (var item in rst) {
                this.ListSource.Remove(item);
            }
            return rst;
        }

        Point p = new Point();

        // Return current hover index, 0-N.
        private int GetCurrentIndex() {
            int rst = IndexFromPoint(p);
            if (rst == -1 && p.Y > Items.Count * ItemHeight) {
                rst = Items.Count;
            }
            return rst;
        }

        private void DrawHintLine() {
            if (currentIndex == -1) { return; }
            int height = ItemHeight * currentIndex;
            RefreshListBox();
            graphic.DrawLine(HintPen, 0, height, Width, height);
        }


        void setDragParams() {
            beginSelectIndex = SelectedIndex;
            selectedIndex = new int[SelectedIndices.Count];
            for (int i = 0; i < selectedIndex.Length; i++) {
                selectedIndex[i] = SelectedIndices[i];
            }
        }


        private Graphics graphic;
        private int[] selectedIndex = null;
        // 重新对 listbox 绘图
        private void RefreshListBox() {
            if (graphic == null) {
                graphic = CreateGraphics();
            }
            graphic.Clear(BackColor);
            Refresh();
        }
        private int beginSelectIndex = -1;
        private int lastSelectedIndex = -1;
        private int currentIndex = -1;
                
    }
}
